// 04_ownership.rs
// Rust 所有权教程

fn main() {
    // 1. 所有权基础概念
    // 变量作用域
    {
        let s = "hello";  // s 在这个作用域中有效
        println!("字符串字面量: {}", s);
    }  // s 在这里超出作用域，不再有效
    
    // 2. String 类型和所有权
    let s11 = "hello";  
    let s21 = s11;  // 所有权转移（移动）
    println!("s11 的值: {:p}", s11.as_ptr());
    println!("s21 的值: {:p}", s21.as_ptr());

    // 2. String 类型和所有权 (堆内存示例)
    let heap_string1 = String::from("hello");  // 在堆上分配内存
    let heap_string2 = heap_string1.clone();   // 克隆，在堆上创建新的内存
    
    println!("heap_string1 堆地址: {:p}", heap_string1.as_ptr());
    println!("heap_string2 堆地址: {:p}", heap_string2.as_ptr());
    
    // 3. 克隆（深拷贝）
    let s3 = String::from("world");
    let s4 = s3.clone();  // 创建堆数据的完整副本
    
    println!("s3 = {}, s4 = {}", s3, s4);
    
    // 4. 栈上数据的拷贝（Copy trait）
    let x = 5;
    let y = x;  // 不会发生所有权转移，因为 i32 实现了 Copy trait
    
    println!("x = {}, y = {}", x, y);
    
    // 5. 函数和所有权
    let s = String::from("hello");  // s 进入作用域
    
    takes_ownership(s);  // s 的值移动到函数里，不再有效
    
    // println!("{}", s);  // 这行会报错，因为 s 已经被移动
    
    let x = 5;  // x 进入作用域
    
    makes_copy(x);  // x 应该移动到函数里，但 i32 实现了 Copy，所以可以继续使用
    
    println!("x 仍然有效: {}", x);
    
    // 6. 返回值和作用域
    let s1 = gives_ownership();  // gives_ownership 将返回值移动给 s1
    println!("从函数获得所有权: {}", s1);
    
    let s2 = String::from("hello");  // s2 进入作用域
    
    let s3 = takes_and_gives_back(s2);  // s2 被移动到函数中，函数返回值又移动到 s3
    println!("转移所有权后: {}", s3);
    
    // 7. 引用和借用
    let s1 = String::from("hello");
    
    let len = calculate_length(&s1);  // 传递引用，不转移所有权
    
    println!("'{}' 的长度是 {}。", s1, len);
    
    // 8. 可变引用
    let mut s = String::from("hello");
    
    change(&mut s);
    println!("修改后的字符串: {}", s);
    
    // 9. 限制可变引用
    let mut s = String::from("hello");
    
    {
        let r1 = &mut s;
        println!("第一个可变引用: {}", r1);
    } // r1 在这里超出作用域，所以可以创建新的引用
    
    let r2 = &mut s;
    println!("第二个可变引用: {}", r2);
    
    // 10. 悬垂引用
    // let reference_to_nothing = dangle();  // 这会导致编译错误
    let string = no_dangle();  // 正确的方式
    println!("非悬垂引用: {}", string);
}

// 以下函数用于演示所有权转移
fn takes_ownership(some_string: String) {  // some_string 进入作用域
    println!("{}", some_string);
}  // some_string 在这里超出作用域，drop 被调用，内存被释放

fn makes_copy(some_integer: i32) {  // some_integer 进入作用域
    println!("{}", some_integer);
}  // some_integer 在这里超出作用域，没有特殊操作

fn gives_ownership() -> String {  // gives_ownership 将返回值移动给调用它的函数
    let some_string = String::from("yours");  // some_string 进入作用域
    
    some_string  // 返回 some_string 并移出调用的函数
}

fn takes_and_gives_back(a_string: String) -> String {  // a_string 进入作用域
    a_string  // 返回 a_string 并移出调用的函数
}

// 引用和借用示例
fn calculate_length(s: &String) -> usize {  // s 是对 String 的引用
    s.len()
}  // s 在这里超出作用域，但由于没有所有权，不会发生任何事

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

// 悬垂引用示例
// fn dangle() -> &String {  // dangle 返回一个字符串的引用
//     let s = String::from("hello");  // s 是一个新字符串
// 
//     &s  // 返回字符串 s 的引用，但 s 在函数结束时离开作用域并被清理！
// }

fn no_dangle() -> String {  // no_dangle 返回一个字符串
    let s = String::from("hello");  // s 是一个新字符串
    
    s  // 返回 s 的所有权，没有问题
}